package com.ruoyi.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ruoyi.config.InvoiceApiConfig;
import com.ruoyi.exception.InvoiceApiException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.*;

@Slf4j
@Component
public class HttpUtils {


    @Resource
    private InvoiceApiConfig apiConfig;

    /**
     * 获取token
     *
     * @return
     */
    private String getToken() {
        String tokenKey = "token";
        String token = InvoiceCacheUtil.get(tokenKey, String.class);
        if (token == null) {
            token = this.token();
        }
        InvoiceCacheUtil.put(tokenKey, token, 600);
        return token;
    }

    private synchronized String token() {
        String url = apiConfig.getTokenUrl().concat(InvoiceApiPath.TOKEN);
        //获取token的请求参数
        Map<String, Object> formData = new HashMap<>();
        formData.put("key", apiConfig.getAppId());
        formData.put("timestamp", String.valueOf(DateUtil.current()));
        formData.put("secret", apiConfig.getSecret());
        formData.put("sign", this.sign(formData));
        formData.remove("secret");
        HttpResponse response = HttpRequest
                .get(url)
                .header("Content-Type", "application/x-www-form-urlencoded")
                .form(formData).execute();
        if (!response.isOk()) {
            InvoiceApiException.createAndThrowIt();
        }
        JSONObject returnObject = JSONUtil.parseObj(response.body());
        if (returnObject.getInt("code") != 200) {
            InvoiceApiException.createAndThrowIt(returnObject.getStr("message"));
        }
        return returnObject.getJSONObject("data").getStr("token");
    }

    public <T> T execute(String url, Object obj, Map<String, String> extHeaders, Class<T> clazz) {
        String requestUtl = apiConfig.getBaseUrl().concat(url);
        String signUrl = requestUtl.substring(apiConfig.getTokenUrl().length());
        String token = this.getToken();
        //生成签名
        Map<String, Object> signMap = new HashMap<>();
        signMap.put("token", token);
        signMap.put("timestamp", String.valueOf(DateUtil.current()));
        signMap.put("url", signUrl);
        //设置header
        Map<String, String> headers = new HashMap<>();
        headers.put("token", token);
        headers.put("timestamp", String.valueOf(DateUtil.current()));
        headers.put("sign", this.sign(signMap));
        headers.put("Content-Type", "application/x-www-form-urlencoded");
        headers.put("Token", token);
        if (extHeaders != null && !extHeaders.isEmpty()) {
            headers.putAll(extHeaders);
        }
        //添加签名
        Map<String, Object> param = BeanUtil.beanToMap(obj, false, false);
        HttpResponse response = HttpRequest
                .post(requestUtl)
                .headerMap(headers, false)
                .form(param).execute();
        if (!response.isOk()) {
            InvoiceApiException.createAndThrowIt(response.body());
        }
        JSONObject returnObject = JSONUtil.parseObj(response.body());
        log.info("接口:{},参数:{},结果:{}", url, param, returnObject);
        if (returnObject.getInt("code") != 200) {
            InvoiceApiException.createAndThrowIt(returnObject.toString());
        }
        return returnObject.toBean(clazz);
    }

    public <T> T executeQd(String url, Object obj, Map<String, String> extHeaders, Class<T> clazz) {
        String requestUtl = apiConfig.getQdBaseUrl().concat(url);
        String signUrl = requestUtl.substring(apiConfig.getTokenUrl().length());
        String token = this.getToken();
        //生成签名
        Map<String, Object> signMap = new HashMap<>();
        signMap.put("token", token);
        signMap.put("timestamp", String.valueOf(DateUtil.current()));
        signMap.put("url", signUrl);
        //设置header
        Map<String, String> headers = new HashMap<>();
        headers.put("token", token);
        headers.put("timestamp", String.valueOf(DateUtil.current()));
        headers.put("sign", this.sign(signMap));
        headers.put("Content-Type", "application/x-www-form-urlencoded");
        headers.put("Token", token);
        if (extHeaders != null && !extHeaders.isEmpty()) {
            headers.putAll(extHeaders);
        }
        //添加签名
        Map<String, Object> param = BeanUtil.beanToMap(obj, false, false);
        HttpResponse response = HttpRequest
                .post(requestUtl)
                .headerMap(headers, false)
                .form(param).execute();
        if (!response.isOk()) {
            InvoiceApiException.createAndThrowIt(response.body());
        }
        JSONObject returnObject = JSONUtil.parseObj(response.body());
        log.info("接口:{},参数:{},结果:{}", url, param, returnObject);
        if (returnObject.getInt("code") != 200) {
            InvoiceApiException.createAndThrowIt(returnObject.toString());
        }
        return returnObject.toBean(clazz);
    }

    public <T> T executeGetQd(String url, Object obj, Map<String, String> extHeaders, Class<T> clazz) {
        String requestUrl = apiConfig.getQdBaseUrl().concat(url);
        String signUrl = requestUrl.substring(apiConfig.getTokenUrl().length());
        String token = this.getToken();
        //生成签名
        Map<String, Object> signMap = new HashMap<>();
        signMap.put("token", token);
        signMap.put("timestamp", String.valueOf(DateUtil.current()));
        signMap.put("url", signUrl);
        //设置header
        Map<String, String> headers = new HashMap<>();
        headers.put("token", token);
        headers.put("timestamp", String.valueOf(DateUtil.current()));
        headers.put("sign", this.sign(signMap));
        headers.put("Token", token);
        if (extHeaders != null && !extHeaders.isEmpty()) {
            headers.putAll(extHeaders);
        }
        //添加签名
        Map<String, Object> param = BeanUtil.beanToMap(obj, false, false);
        String finalUrlAndData = requestUrl.concat("?").concat(getGetData(param));
        HttpResponse response = HttpRequest
                .get(finalUrlAndData)
                .headerMap(headers, false).execute();
        if (!response.isOk()) {
            InvoiceApiException.createAndThrowIt(response.body());
        }
        JSONObject returnObject = JSONUtil.parseObj(response.body());
        log.info("Get请求最终Url：{}",finalUrlAndData);
        log.info("接口:{},参数:{},结果:{}", url, param, returnObject);
        if (returnObject.getInt("code") != 200) {
            InvoiceApiException.createAndThrowIt(returnObject.toString());
        }
        return returnObject.toBean(clazz);
    }

    public InputStream executeGetStream(String url, Object obj, Map<String, String> extHeaders) {
        String requestUrl = apiConfig.getBaseUrl().concat(url);
        String signUrl = requestUrl.substring(apiConfig.getTokenUrl().length());
        String token = this.getToken();
        //生成签名
        Map<String, Object> signMap = new HashMap<>();
        signMap.put("token", token);
        signMap.put("timestamp", String.valueOf(DateUtil.current()));
        signMap.put("url", signUrl);
        //设置header
        Map<String, String> headers = new HashMap<>();
        headers.put("token", token);
        headers.put("timestamp", String.valueOf(DateUtil.current()));
        headers.put("sign", this.sign(signMap));
        headers.put("Content-Type", "application/x-www-form-urlencoded");
        if (extHeaders != null && !extHeaders.isEmpty()) {
            headers.putAll(extHeaders);
        }
        //添加签名
        Map<String, Object> param = BeanUtil.beanToMap(obj, false, false);
        HttpResponse response = HttpRequest
                .post(requestUrl)
                .headerMap(headers, false)
                .form(param).execute();
        if (!response.isOk()) {
            InvoiceApiException.createAndThrowIt(response.body());
        }
        return response.bodyStream();
    }

    public <T> T executeJson(String url, Object obj, Map<String, String> extHeaders, Class<T> clazz) {
        String requestUtl = apiConfig.getBaseUrl().concat(url);
        String signUrl = requestUtl.substring(apiConfig.getTokenUrl().length());
        String token = this.getToken();
        //生成签名
        Map<String, Object> signMap = new HashMap<>();
        signMap.put("token", token);
        signMap.put("timestamp", String.valueOf(DateUtil.current()));
        signMap.put("url", signUrl);
        //设置header
        Map<String, String> headers = new HashMap<>();
        headers.put("token", token);
        headers.put("timestamp", String.valueOf(DateUtil.current()));
        headers.put("sign", this.sign(signMap));
        headers.put("Content-Type", "application/json");
        if (extHeaders != null && !extHeaders.isEmpty()) {
            headers.putAll(extHeaders);
        }
        //添加签名
        HttpResponse response = HttpRequest
                .post(requestUtl)
                .headerMap(headers, false)
                .body(JSONUtil.toJsonStr(obj)).execute();
        if (!response.isOk()) {
            InvoiceApiException.createAndThrowIt();
        }
        JSONObject returnObject = JSONUtil.parseObj(response.body());
        log.info("接口:{},参数:{},结果:{}", url, obj, returnObject);
        if (returnObject.getInt("code") != 200) {
            InvoiceApiException.createAndThrowIt(returnObject.toString());
        }
        return returnObject.getBean("data", clazz);
    }

    private String sign(Map<String, Object> params) {
        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);
        StringBuilder sb = new StringBuilder();
        for (String k : keys) {
            String v = params.get(k) == null ? "" : String.valueOf(params.get(k));
            if (v != null && !v.isEmpty()) {
                sb.append(k).append("=").append(v).append("&");
            }
        }
        String signStr = sb.toString();
        signStr = signStr.substring(0, signStr.length() - 1);
        return DigestUtil.md5Hex(signStr).toUpperCase();
    }

    private String getGetData(Map<String, Object> param){
        StringBuffer stringBuffer = new StringBuffer();
        for (String key : param.keySet()) {
            stringBuffer.append(key).append("=").append(Optional.ofNullable(param.get(key)).orElse("")).append("&");
        }
        String data = stringBuffer.toString();
        return data.substring(0,data.length()-1);
    }

}

